{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Code Editor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| default_exp code_editor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Todos\n",
    "- ~~Full page editable area for writing code~~\n",
    "- ~~Syntax highlighting~~\n",
    "- ~~Line number side bar~~\n",
    "- auto-language detection\n",
    "- inline autocompletion\n",
    "- ~~save files~~\n",
    "- ~~handle multiple files~~\n",
    "- execute code and show output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "from fasthtml.fastapp import *\n",
    "\n",
    "# Ace Editor (https://ace.c9.io/)\n",
    "ace_editor = Script(src=\"https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.0/ace.min.js\")\n",
    "# Flexbox CSS (http://flexboxgrid.com/)\n",
    "gridlink = Link(rel=\"stylesheet\", href=\"https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css\", type=\"text/css\")\n",
    "\n",
    "css = Style('''\\\n",
    ".sidebar {\n",
    "    background-color: #f4f4f4;\n",
    "    overflow-y: auto;\n",
    "    padding: 10px;\n",
    "    box-shadow: 2px 0 5px rgba(0,0,0,0.1);\n",
    "    height: calc(100vh - 40px);\n",
    "}\n",
    "\n",
    "#editor-container {\n",
    "    flex: 1;\n",
    "    height: calc(100vh - 40px);\n",
    "}\n",
    "\n",
    "#editor {\n",
    "    height: 100%;\n",
    "    width: 100%;\n",
    "}\n",
    "\n",
    ".box-row {\n",
    "    border: 1px solid #ccc;\n",
    "}\n",
    "''')\n",
    "\n",
    "app,rt,files, File = fast_app('data/files.db', hdrs=(ace_editor, gridlink, css), id=int, filename=str, content=str, pk='id')\n",
    "\n",
    "id_curr = 'current-file'\n",
    "id_list = 'file-list'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "files.insert(File(filename='file1.txt', content='Hello, World!'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "js_code = \"\"\"\\\n",
    "function renderEditor() {\n",
    "    var editor = ace.edit(\"editor\");\n",
    "    editor.setTheme(\"ace/theme/monokai\");\n",
    "    editor.session.setMode(\"ace/mode/javascript\");\n",
    "}\n",
    "\n",
    "function getFileContent() {\n",
    "    var editor = ace.edit(\"editor\");\n",
    "    return editor.getValue();\n",
    "}\n",
    "\n",
    "renderEditor();\n",
    "\"\"\"\n",
    "\n",
    "example_code = \"\"\"\\\n",
    "function foo(items) {\n",
    "    var x = \"All this is syntax highlighted\";\n",
    "    return x;\n",
    "}\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def SaveFile():\n",
    "    return Form(\n",
    "        Input(type=\"text\", id=\"filename\", name=\"filename\", placeholder=\"Filename\", required=True),\n",
    "        Button(\"Save\", type=\"submit\", hx_post=\"/save\", target_id=id_list, hx_swap=\"beforeend\", hx_vals=\"js:{content: getFileContent(), filename: filename.value}\"),\n",
    "        cls=\"col-xs-12\"\n",
    "    )\n",
    "    ...\n",
    "def Toolbar():\n",
    "    return Div(\n",
    "        Div(\n",
    "            Select(\n",
    "                Option(\"JavaScript\", value=\"javascript\"),\n",
    "                Option(\"Python\", value=\"python\"),\n",
    "                Option(\"HTML\", value=\"html\"),\n",
    "                Option(\"CSS\", value=\"css\"),\n",
    "                Option(\"Markdown\", value=\"markdown\"),\n",
    "                id=\"language\"\n",
    "            ),\n",
    "            Button(\"Run\", id=\"run\"),\n",
    "            SaveFile(),\n",
    "            cls=\"col-xs-12 toolbar\"\n",
    "        ),\n",
    "        cls=\"row\"\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def FileRow(file: File):\n",
    "    return Li(\n",
    "        A(\n",
    "            file.filename, hx_get=f'/files/{file.id}', target_id=\"editor-container\", hx_swap=\"innerHTML\",\n",
    "            hx_on=\"htmx:afterSwap: renderEditor()\",\n",
    "          ),\n",
    "        id=f'file-{file.id}'\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def Sidebar():\n",
    "    return Div(\n",
    "        Div(\n",
    "            Ul(*map(FileRow, files()), id=id_list), cls=\"sidebar\"\n",
    "        ),\n",
    "        cls=\"col-xs-12 col-sm-3\"\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def CodeEditor():\n",
    "    toolbar = Toolbar()\n",
    "    main = Div(\n",
    "        Sidebar(),\n",
    "        Div(\n",
    "            Div(example_code, id=\"editor\"),\n",
    "            id=\"editor-container\", cls=\"col-xs-12 col-sm-9\", hx_on=\"htmx:afterSwap: renderEditor()\"\n",
    "        ),\n",
    "        cls=\"row\"\n",
    "    )\n",
    "    return Title(\"Code Editor\",), Div(toolbar, main, cls=\"container-fluid\"), Script(NotStr(js_code))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<title>Code Editor</title>\n",
       "\n",
       "<div id=\"editor\" style=\"height: 100%; width: 100%;\">function foo(items) {\n",
       "    var x = &quot;All this is syntax highlighted&quot;;\n",
       "    return x;\n",
       "}\n",
       "</div>\n",
       "\n",
       "<script>var editor = ace.edit(&quot;editor&quot;);\n",
       "editor.setTheme(&quot;ace/theme/monokai&quot;);\n",
       "editor.session.setMode(&quot;ace/mode/javascript&quot;);\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show(CodeEditor())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@rt(\"/\")\n",
    "def get():\n",
    "    return CodeEditor()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@rt(\"/files/{id}\")\n",
    "def get(id:int):\n",
    "    return Div(files[id].content, id=\"editor\", cls=\"ace_editor ace-tm\")#, hx_on=\"htmx:afterSwap: renderEditor()\"),"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "@rt(\"/save\")\n",
    "def post(filename: str, content: str):\n",
    "    file = File(filename=filename, content=content, id=len(files()) + 1)\n",
    "    files.insert(file)\n",
    "    return FileRow(file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| eval: false\n",
    "#| hide\n",
    "from nbdev.export import nb_export\n",
    "nb_export('code_editor.ipynb', '.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
